﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace Monitor
{
    public partial class FormDiagram : Form
    {
        public delegate int GetDataCallback();

        public class Data
        {
            public string[] Names;
            public Color[] Colors;
            public Rectangle Rectangle;
            public GetDataCallback[] GetDataCallbacks;
            public Queue<Point>[] Datas;

            //水平线
            int MaxData = 100;
            int MinData = 0;
            int ZeroData = 0;

            //值
            int[] Max, Min, Average, Sum;
            int Ticks;

            public Data(string name, Color color, Rectangle rectangle, GetDataCallback getData)
            {
                this.Datas = new Queue<Point>[] { new Queue<Point>() };

                this.Max = new int[1];
                this.Min = new int[1];
                this.Average = new int[1];
                this.Sum = new int[1];

                this.Names = new string[] { name };
                this.Colors = new Color[] { color };
                this.Rectangle = rectangle;
                this.GetDataCallbacks = new GetDataCallback[] { getData };
                this.MaxData = rectangle.Height;
            }

            public Data(string[] names, Color[] colors, Rectangle rectangle, params GetDataCallback[] getDatas)
            {
                int length = Math.Min(Math.Min(names.Length, getDatas.Length), colors.Length);

                this.Datas = new Queue<Point>[length];
                this.Max = new int[length];
                this.Min = new int[length];
                this.Average = new int[length];
                this.Sum = new int[length];

                for (int i = 0; i < this.Datas.Length; i++)
                {
                    this.Datas[i] = new Queue<Point>();
                }

                this.Names = names;
                this.Colors = colors;
                this.Rectangle = rectangle;
                this.GetDataCallbacks = getDatas;
                this.MaxData = rectangle.Height;
            }

            public void GetData()
            {
                this.Ticks++;

                for (int i = 0; i < this.Datas.Length; i++)
                {
                    int data = this.GetDataCallbacks[i]();

                    if (this.Ticks == 1)
                    {
                        this.Max[i] = data;
                        this.Min[i] = data;
                        this.Sum[i] = data;
                        this.Average[i] = data;
                    }
                    else
                    {
                        this.Max[i] = Math.Max(this.Max[i], data);
                        this.Min[i] = Math.Min(this.Min[i], data);
                        this.Sum[i] += data;
                        this.Average[i] = this.Sum[i] / this.Ticks;
                    }

                    this.MaxData = Math.Max(this.MaxData, data);
                    this.MinData = Math.Min(this.MinData, data);
                    this.ZeroData = this.Rectangle.Top + this.MaxData * this.Rectangle.Height / (this.MaxData - this.MinData);
                    this.Datas[i].Enqueue(new Point(this.Ticks, data));
                }

            }

            Point PointMouse = new Point();
            public void DrawData(Graphics G, Point pointMouse, bool bMouseDown)
            {

                Font font = new Font("宋体", 9f);

                G.DrawRectangle(Pens.Green, this.Rectangle);
                G.DrawLine(Pens.DarkRed, this.Rectangle.Left, ZeroData, this.Rectangle.Right, ZeroData);
                G.DrawString("0", font, Brushes.DarkRed, this.Rectangle.Right, ZeroData);

                if (bMouseDown)
                    this.PointMouse.X--;
                else
                    this.PointMouse = pointMouse;

                for (int i = 0; i < this.Datas.Length; i++)
                {
                    if (this.Datas[i].Count > this.Rectangle.Width) this.Datas[i].Dequeue();

                    string strMaxMin = string.Format("{0}:Max={1},Min={2},Average={3},Ticks={4}", this.Names[i], this.Max[i], this.Min[i], this.Average[i], this.Ticks);
                    SizeF sizeMaxMin = G.MeasureString(strMaxMin, font);
                    G.DrawString(strMaxMin, font, Brushes.Red, new RectangleF(new PointF(this.Rectangle.Right - sizeMaxMin.Width, this.Rectangle.Bottom + i * 10), sizeMaxMin));

                    Point[] datas = this.Datas[i].ToArray();

                    if (datas.Length > 0)
                    {
                        Point p1 = new Point(Rectangle.Right - this.Datas[i].Count, this.Rectangle.Top + (this.MaxData - datas[0].Y) * this.Rectangle.Height / (this.MaxData - this.MinData));

                        for (int j = Rectangle.Right - this.Datas[i].Count; j < Rectangle.Right; j++)
                        {
                            Point p2 = new Point(j, this.Rectangle.Top + (this.MaxData - datas[j - Rectangle.Right + this.Datas[i].Count].Y) * this.Rectangle.Height / (this.MaxData - this.MinData));

                            G.DrawLine(new Pen(this.Colors[i]), p1, p2);

                            p1 = p2;
                        }

                        G.DrawString(this.Names[i], font, new SolidBrush(this.Colors[i]), this.Rectangle.Location);
                        string strData = string.Format("{0}", datas[datas.Length - 1].Y);
                        G.DrawString(strData, font, new SolidBrush(this.Colors[i]), new RectangleF(p1, G.MeasureString(strData, font)));

                        

                        if (this.PointMouse.X >= this.Rectangle.Right - this.Datas[i].Count
                            && this.PointMouse.X < this.Rectangle.Right)
                        {
                            Point mouse = new Point(this.PointMouse.X, this.Rectangle.Top + (this.MaxData - datas[this.PointMouse.X - Rectangle.Right + this.Datas[i].Count].Y) * this.Rectangle.Height / (this.MaxData - this.MinData) - 1);
                            G.DrawLine(Pens.DarkSeaGreen, this.Rectangle.Left, mouse.Y, this.Rectangle.Right, mouse.Y);
                            G.DrawLine(Pens.DarkSeaGreen, mouse.X, this.Rectangle.Top, mouse.X, this.Rectangle.Bottom);

                            G.DrawString(datas[this.PointMouse.X - Rectangle.Right + this.Datas[i].Count].Y.ToString(), font, Brushes.DarkSeaGreen, mouse);
                        }
                    }
                }
            }
        }

        Data[] Datas;

        Timer Timer = new Timer();

        public FormDiagram(string text, params Data[] datas)
        {
            this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw | ControlStyles.UserPaint, true);

            this.Datas = datas;

            InitializeComponent();

            this.Text = text;

            Timer.Interval = 50;
            Timer.Tick += new EventHandler(Timer_Tick);
        }

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);

            Timer.Start();
        }

        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);

            foreach (Data data in Datas)
            {
                data.Rectangle.X = this.ClientSize.Width - data.Rectangle.Width - 50;
            }
        }

        Point pointMouse = new Point();
        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);

            if (!bMouseDown) pointMouse = e.Location;
        }

        bool bMouseDown = false;
        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);

            bMouseDown = true;
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            base.OnMouseUp(e);

            bMouseDown = false;
        }

        void Timer_Tick(object sender, EventArgs e)
        {
            foreach (Data data in this.Datas)
            {
                data.GetData();
            }

            this.Invalidate();
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);

            Graphics G = e.Graphics;

            foreach (Data data in this.Datas)
            {
                data.DrawData(G, pointMouse, bMouseDown);
            }
        }
    }
}
